package com.liuym.admin.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.ListObjectsRequest;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.OSSObjectSummary;
import com.aliyun.oss.model.ObjectListing;
import com.liuym.admin.properties.OssProperties;
import com.liuym.admin.repository.mapper.ExportLogMapper;
import com.liuym.admin.repository.model.bo.AdminBO;
import com.liuym.admin.repository.model.entity.ExportLog;
import com.liuym.admin.repository.model.vo.AdminVO;
import com.liuym.admin.repository.model.vo.ExportLogVO;
import com.liuym.admin.service.AdminService;
import com.liuym.admin.service.ImportAndExportService;
import com.liuym.admin.service.TableNameService;
import com.liuym.common.annotation.AdminLogin;
import com.liuym.common.annotation.AdminLoginUser;
import com.liuym.common.constants.Constants;
import com.liuym.common.constants.GradeResponse;
import com.liuym.common.dubbo.classes.api.ClassScoreDubboService;
import com.liuym.common.dubbo.classes.dto.ClassScoreDTO;
import com.liuym.common.dubbo.student.api.StudentScoreDubboService;
import com.liuym.common.dubbo.student.dto.StudentScoreDTO;
import com.liuym.common.exception.AdminException;
import com.liuym.common.exception.ClassException;
import com.liuym.common.exception.StudentException;
import com.liuym.common.utils.DateUtil;
import com.liuym.common.utils.FileUtils;
import com.vip.vjtools.vjkit.mapper.BeanMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.zip.Adler32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author Liuym
 * @date 2018/11/30 11:10
 * @describe：
 */
@AdminLogin
@RestController
@RequestMapping("/admin")
public class AdminController {

    private static Logger logger = LoggerFactory.getLogger(AdminController.class);


    private static String UPLOADED_FOLDER = "//Users//liuyiming//temp";
    @Autowired
    private AdminService adminService;
    @Autowired
    private OssProperties ossProperties;
    @Autowired
    private ImportAndExportService importAndExportService;

    @Autowired
    private ExportLogMapper exportLogMapper;

    @Reference(version = "0.0.1", timeout = 20000)
    private StudentScoreDubboService studentScoreDubboService;

    @Reference(version = "0.0.1", timeout = 20000)
    private ClassScoreDubboService classScoreDubboService;
    @Autowired
    private TableNameService tableNameService;

    @GetMapping("/findAdmin")
    public GradeResponse findAdmin(@AdminLoginUser AdminVO adminVO) {
        if (adminVO != null) {
            return GradeResponse.success(adminVO);
        }
        return GradeResponse.internalServerError();
    }

    @GetMapping("/findAdminByUserName")
    public GradeResponse findClassByClassNumber(String userName) {
        AdminBO adminBO = new AdminBO();
        try {
            adminBO = adminService.getAdminByUserName(userName);
        } catch (AdminException e) {
            logger.error("error", e);
            return GradeResponse.internalServerError();
        }
        return GradeResponse.success(adminBO);
    }

    @PutMapping("/updateAdmin")
    public GradeResponse updateAdmin(AdminVO adminVO) {

        try {
            if (adminVO != null) {
                AdminBO adminBO = BeanMapper.map(adminVO, AdminBO.class);
                adminService.updateAdmin(adminBO);
            }
        } catch (Exception e) {
            if (e instanceof AdminException) {
                logger.warn("warn", e);
                return GradeResponse.fail(((AdminException) e).getCode(), ((AdminException) e).getMsg());
            }
            logger.error("error", e);
            return GradeResponse.internalServerError();
        }
        return GradeResponse.success();
    }

    @GetMapping("/findAllAdmin")
    public GradeResponse findAllAdmin() {
        List<AdminBO> allAdmin = adminService.getAllAdmin();
        return GradeResponse.success(allAdmin);
    }

    @DeleteMapping("/deleteAdmin")
    public GradeResponse deleteAdmin(int id) {

        try {
            adminService.deleteAdminById(id);
        } catch (AdminException e) {
            logger.error("error", e);
            return GradeResponse.internalServerError();
        }
        return GradeResponse.success();
    }

    @PostMapping("/addAdmin")
    public GradeResponse addAdmin(AdminVO adminVO) {
        AdminBO adminBO = null;
        if (adminVO != null) {
            adminVO.setAccess(0);
            adminBO = BeanMapper.map(adminVO, AdminBO.class);
        }
        adminService.addAdmin(adminBO);
        return GradeResponse.success();
    }

    @GetMapping("/findStudentScore")
    public GradeResponse findStudentScore(@AdminLoginUser AdminVO adminVO, @RequestParam("startTime") String starTime, @RequestParam("endTime") String endTime) {

        List<StudentScoreDTO> studentScoreList = new ArrayList<>();

        if (adminVO != null) {
            try {
                if (adminVO.getAccess() == 1) {

                    studentScoreList = studentScoreDubboService.getStudentScoreByTime(tableNameService.findByTableAndTime(
                            Constants.STUDENT_SCORE_TABLE_NAME).getTableName(), DateUtil.stringToDate(starTime, "yyyy-MM-dd"),
                            DateUtil.stringToDate(endTime, "yyyy-MM-dd"));
                } else {
                    studentScoreList = studentScoreDubboService.getStudentScoreByAdmin(tableNameService.findByTableAndTime(
                            Constants.STUDENT_SCORE_TABLE_NAME).getTableName(), adminVO.getUserName(),
                            DateUtil.stringToDate(starTime, "yyyy-MM-dd"),
                            DateUtil.stringToDate(endTime, "yyyy-MM-dd"));
                }
            } catch (StudentException e) {
                logger.error("调用studentScoreDubboService失败：getStudentScoreByTime/getStudentScoreByAdmin", e);
                return GradeResponse.internalServerError();
            }

        }
        return GradeResponse.success(studentScoreList);
    }

    @GetMapping("/findClassScore")
    public GradeResponse findClassScore(@AdminLoginUser AdminVO adminVO, @RequestParam("startTime") String starTime, @RequestParam("endTime") String endTime) {

        List<ClassScoreDTO> scoreDTOList = new ArrayList<>();

        if (adminVO != null) {
            try {
                if (adminVO.getAccess() == 1) {

                    scoreDTOList = classScoreDubboService.getClassScoreByTime(tableNameService.findByTableAndTime(
                            Constants.CLASS_SCORE_TABLE_NAME).getTableName(), DateUtil.stringToDate(starTime, "yyyy-MM-dd"),
                            DateUtil.stringToDate(endTime, "yyyy-MM-dd"));
                } else {
                    scoreDTOList = classScoreDubboService.getClassScoreByAdmin(tableNameService.findByTableAndTime(
                            Constants.CLASS_SCORE_TABLE_NAME).getTableName(), adminVO.getUserName(),
                            DateUtil.stringToDate(starTime, "yyyy-MM-dd"),
                            DateUtil.stringToDate(endTime, "yyyy-MM-dd"));
                }
            } catch (ClassException e) {
                logger.error("调用studentScoreDubboService失败：getStudentScoreByTime/getStudentScoreByAdmin", e);
                return GradeResponse.internalServerError();
            }

        }
        return GradeResponse.success(scoreDTOList);
    }

    @PostMapping("/upload")
    public GradeResponse upload(
            @AdminLoginUser AdminVO adminVO, @RequestParam("type") String type, @RequestParam("file") MultipartFile uploadfile) {
        logger.debug("Single file upload!");
        Map<String, Object> result = null;
        if (uploadfile.isEmpty()) {
            return GradeResponse.success("请选择文件!");
        }
        try {
            String fileName = saveUploadedFiles(type, uploadfile, adminVO.getUserName());
            try {
                result = importAndExportService.importData(fileName, type, adminVO.getUserName());
            } catch (AdminException e) {
                logger.error("error", e);
                return GradeResponse.fail(e.getCode(), e.getMsg());
            }
        } catch (IOException e) {
            logger.error("error", e);
            return GradeResponse.internalServerError();
        }
        return GradeResponse.success(result);
    }

    @PostMapping("/export")
    public GradeResponse export(@AdminLoginUser AdminVO adminVO, @RequestParam("exportInfo") String exportInfo,
                                @RequestParam("year") int year, @RequestParam("classNumber") long classNumber) {
        try {
            importAndExportService.exportData(exportInfo, year, classNumber, adminVO.getUserName());
        } catch (AdminException e) {
            logger.error("error", e);
            return GradeResponse.fail(e.getCode(), e.getMsg());
        }
        return GradeResponse.success();
    }

    private String saveUploadedFiles(String type, MultipartFile file, String userName) throws IOException {
        OSSClient ossClient = new OSSClient(ossProperties.endpoint,
                ossProperties.accessKeyId, ossProperties.accessKeySecret);
        byte[] bytes = file.getBytes();
        String oldFileName = file.getOriginalFilename();
        String newFileName = type + "_" + System.currentTimeMillis() + "." + oldFileName.substring(oldFileName.lastIndexOf(".") + 1);
        Path path = Paths.get(UPLOADED_FOLDER + newFileName);
        Files.write(path, bytes);
        Calendar now = Calendar.getInstance();
        String basePath = "grade/" + type + "/";
        String uploadName = type + "导入数据" + DateUtil.DateToString(new Date(), "yyyy-MM-dd") + "-" + UUID.randomUUID().toString().substring(1, 8) +
                "-" + userName;
        String uploadFilePath = FileUtils.getObjectName(basePath, uploadName, "xlsx");
        ossClient.putObject(ossProperties.bucketName, uploadFilePath, new File(path.toString()));
        ossClient.shutdown();
        ExportLog exportLog = new ExportLog();
        exportLog.setTitle(uploadName);
        exportLog.setPath(uploadFilePath);
        exportLog.setUserName(userName);
        exportLog.setDate(new Date());
        exportLog.setType(1);
        exportLogMapper.insertSelective(exportLog);
        return path.toString();
    }

    @GetMapping("/fileList")
    public GradeResponse fileList(@AdminLoginUser AdminVO adminVO, Integer type) {
        List<ExportLogVO> exportLogVOList = new ArrayList<>();
        List<ExportLog> list = new ArrayList<>();
        if (adminVO.getAccess() == 1) {
            list = exportLogMapper.findList(type);
        } else {
            list = exportLogMapper.findListByUserName(adminVO.getUserName(), type);
        }
        if (list != null && list.size() > 0) {
            list.stream().forEach(exportLog -> {
                ExportLogVO exportLogVO = new ExportLogVO();
                exportLogVO.setDate(DateUtil.DateToString(exportLog.getDate(), "yyyy-MM-dd HH:mm:ss"));
                exportLogVO.setId(exportLog.getId());
                exportLogVO.setPath(exportLog.getPath());
                exportLogVO.setTitle(exportLog.getTitle());
                exportLogVO.setUserName(exportLog.getUserName());
                exportLogVOList.add(exportLogVO);
            });
        }

        return GradeResponse.success(exportLogVOList);
    }

    @PostMapping("/download")
    public GradeResponse download(@AdminLoginUser AdminVO adminVO, @RequestParam("id") int id) {
        ExportLog exportLog = exportLogMapper.selectByPrimaryKey(id);
        String download = adminService.download(exportLog.getPath(), exportLog.getTitle());
        return GradeResponse.success(download);
    }


    @PostMapping("/logout")
    public GradeResponse logout(@RequestParam String token) {
        adminService.logout(token);
        return GradeResponse.success("操作成功", "");
    }

    @GetMapping("/downloadAll")
    public void zipFilesDown(HttpServletRequest request,
                             HttpServletResponse response,
                             @RequestParam("downloadType") String downloadType,
                             @RequestParam("downFileName") String downFileName) {
        //阿里云基础配置
        try {
            // 初始化
            OSSClient ossClient = new OSSClient(ossProperties.endpoint,
                    ossProperties.accessKeyId, ossProperties.accessKeySecret);
            ;
            String fileName = downFileName + ".zip";
            // 创建临时文件
            File zipFile = File.createTempFile(downFileName, ".zip");
            FileOutputStream f = new FileOutputStream(zipFile);

            CheckedOutputStream csum = new CheckedOutputStream(f, new Adler32());
            // 用于将数据压缩成Zip文件格式
            ZipOutputStream zos = new ZipOutputStream(csum);

            // 构造ListObjectsRequest请求。
            ListObjectsRequest listObjectsRequest = new ListObjectsRequest(ossProperties.bucketName);
            // 列出userId目录下的所有文件和文件夹。
            listObjectsRequest.setPrefix("grade/" + downloadType + "/exportData");
            ObjectListing listing = ossClient.listObjects(listObjectsRequest);
            // 遍历所有文件。
            System.out.println("Objects:");
            for (OSSObjectSummary ossObjectSummary : listing.getObjectSummaries()) {
                System.out.println(ossObjectSummary.getKey());
                String eachFileName = ossObjectSummary.getKey();
                // 获取Object，返回结果为OSSObject对象
                OSSObject ossObject = ossClient.getObject(ossProperties.bucketName, ossObjectSummary.getKey());
                // 读去Object内容  返回
                InputStream inputStream = ossObject.getObjectContent();
                // 对于每一个要被存放到压缩包的文件，都必须调用ZipOutputStream对象的putNextEntry()方法，确保压缩包里面文件不同名
                zos.putNextEntry(new ZipEntry(eachFileName));
                int bytesRead;
                // 向压缩文件中输出数据
                while ((bytesRead = inputStream.read()) != -1) {
                    zos.write(bytesRead);
                }
                inputStream.close();
                zos.closeEntry(); // 当前文件写完，定位为写入下一条项目
            }
            zos.close();
            String header = request.getHeader("User-Agent").toUpperCase();
            if (header.contains("MSIE") || header.contains("TRIDENT") || header.contains("EDGE")) {
                fileName = URLEncoder.encode(fileName, "utf-8");
                fileName = fileName.replace("+", "%20");    //IE下载文件名空格变+号问题
            } else {
                fileName = new String(fileName.getBytes(), "ISO8859-1");
            }
            response.reset();
            response.setContentType("text/plain");
            response.setContentType("application/octet-stream; charset=utf-8");
            response.setHeader("Location", fileName);
            response.setHeader("Cache-Control", "max-age=0");
            response.setHeader("Content-Disposition", "attachment; filename=" + fileName);

            FileInputStream fis = new FileInputStream(zipFile);
            BufferedInputStream buff = new BufferedInputStream(fis);
            BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
            byte[] car = new byte[1024];
            int l = 0;
            while (l < zipFile.length()) {
                int j = buff.read(car, 0, 1024);
                l += j;
                out.write(car, 0, j);
            }
            // 关闭流
            fis.close();
            buff.close();
            out.close();

            ossClient.shutdown();
            // 删除临时文件
            zipFile.delete();
        } catch (Exception e) {
            e.printStackTrace();

        }
    }

}
